package uns.fit.gis.qwarper;

import uns.fit.gis.mapobj.Point;

public class QWarper {
	private Matrix warpFactors;
	private boolean isPerspective;
	private final double EPSILON = 1.0e-18f;
	private final int VALUE_OVERFLOW = 0;
	
	public QWarper(Quad destQuad, Quad sourceQuad, boolean isPerspective) {
		try {
			this.isPerspective = isPerspective;
			preCalc(destQuad, sourceQuad);
			warpFactors.show();
		}
		catch(Exception exception) {
			exception.printStackTrace();
		}
	}
	private void preCalc(Quad destQuad, Quad sourceQuad) throws Exception{
		if(isPerspective) {
			Matrix matrixLeft = new Matrix(8, 8);
			Matrix matrixRight = new Matrix(8, 1);
			
			int row = 0;
			for(int i = 0; i < 4; i++) {
				matrixLeft.set(row, 0, 1.0);
				matrixLeft.set(row, 1, sourceQuad.getCorner(i).getX());
				matrixLeft.set(row, 2, sourceQuad.getCorner(i).getY());
				
				matrixLeft.set(row, 6, -sourceQuad.getCorner(i).getX() * destQuad.getCorner(i).getX());
				matrixLeft.set(row, 7, -sourceQuad.getCorner(i).getY() * destQuad.getCorner(i).getX());
				
				matrixRight.set(row, 0, destQuad.getCorner(i).getX());
				
				row ++;
				
				matrixLeft.set(row, 3, 1.0);
				matrixLeft.set(row, 4, sourceQuad.getCorner(i).getX());
				matrixLeft.set(row, 5, sourceQuad.getCorner(i).getY());
				
				matrixLeft.set(row, 6, -sourceQuad.getCorner(i).getX() * destQuad.getCorner(i).getY());
				matrixLeft.set(row, 7, -sourceQuad.getCorner(i).getY() * destQuad.getCorner(i).getY());
				
				matrixRight.set(row, 0, destQuad.getCorner(i).getY());
				
				row ++;
			}
			warpFactors = matrixLeft.solve(matrixRight);
		}
		else {
			Matrix matrixLeft = new Matrix(4, 4);
			Matrix matrixRight = new Matrix(4, 2);
			
			for(int row = 0; row < 4; row++) {
				matrixLeft.set(row, 0, 1.0);
				matrixLeft.set(row, 1, sourceQuad.getCorner(row).getX() * sourceQuad.getCorner(row).getY());
				matrixLeft.set(row, 2, sourceQuad.getCorner(row).getX());
				matrixLeft.set(row, 3, sourceQuad.getCorner(row).getX());
				
				matrixRight.set(row, 0, destQuad.getCorner(row).getX());
				matrixRight.set(row, 1, destQuad.getCorner(row).getY());
			}
			warpFactors = matrixLeft.solve(matrixRight);
		}
	}
	/*
	public Point warpPoint(Point point) {
		double num = warpFactors.get(6, 0) * point.getX() +
			warpFactors.get(7, 0) * point.getY() + 1.0;
		double x = (warpFactors.get(0, 0) +
			warpFactors.get(1, 0) * point.getX() +
			warpFactors.get(2, 0) * point.getY()) / num;
		double y = (warpFactors.get(3, 0) +
				warpFactors.get(4, 0) * point.getX() +
				warpFactors.get(5, 0) * point.getY()) / num;
		return new Point((int)x, (int)y);
	}
	*/
	/*
	public Point warpPoint(int x, int y) {
		double num = warpFactors.get(6, 0) * x +
			warpFactors.get(7, 0) * y + 1.0;
		double newX = (warpFactors.get(0, 0) +
			warpFactors.get(1, 0) * x +
			warpFactors.get(2, 0) * y) / num;
		double newY = (warpFactors.get(3, 0) +
				warpFactors.get(4, 0) * x +
				warpFactors.get(5, 0) * y) / num;
		return new Point((int)newX, (int)newY);
	}
	*/
	public int warpPoint(Point point) {
		int x = point.getX();
		int y = point.getY(); 
		double num = warpFactors.get(6, 0) * x +
			warpFactors.get(7, 0) * y + 1.0;
		//System.out.println("---NUM_WARP = " + num);
		//if (Math.abs(num) < EPSILON)
		if (num < EPSILON)
			return VALUE_OVERFLOW;
		double newX = (warpFactors.get(0, 0) +
			warpFactors.get(1, 0) * x +
			warpFactors.get(2, 0) * y) / num;
		double newY = (warpFactors.get(3, 0) +
				warpFactors.get(4, 0) * x +
				warpFactors.get(5, 0) * y) / num;
		point.setX((int)newX);
		point.setY((int)newY);
		return 1;
	}
	
	public int warpPoints(int[] xs, int[] ys) {
		int ret = 0;
		for(int i = 0; i < xs.length; i++) {
			Point point = new Point(xs[i], ys[i]);
			ret += warpPoint(point);
			xs[i] = point.getX();
			ys[i] = point.getY();
		}
		return ret;
	}

	/*
	public int warpX(int x, int y) {
		int INSET_X = 30;
		int INSET_Y = 80;
		int EXTRA_LOWER_EDGE = 300;
		int screenWidth = 240;
		int screenHeight = 320;
		
		Point point = Util.corPix(INSET_X, INSET_Y,
				screenWidth - INSET_X, INSET_Y,
				screenWidth + EXTRA_LOWER_EDGE, screenHeight,
				-EXTRA_LOWER_EDGE, screenHeight, x, y);
		return point.getX();
	}
	
	public int warpY(int x, int y) {
		int INSET_X = 30;
		int INSET_Y = 80;
		int EXTRA_LOWER_EDGE = 300;
		int screenWidth = 240;
		int screenHeight = 320;
		
		Point point = Util.corPix(INSET_X, INSET_Y,
				screenWidth - INSET_X, INSET_Y,
				screenWidth + EXTRA_LOWER_EDGE, screenHeight,
				-EXTRA_LOWER_EDGE, screenHeight, x, y);
		return point.getY();
	}
	*/
}
